/* main.c - Application main entry point */

/*
 * Copyright (c) 2022 Xiaomi Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <zephyr/types.h>
#include <zephyr.h>
#include <stdio.h>
#include <device.h>
#include <soc.h>
#include <settings/settings.h>

#include <bluetooth/bluetooth.h>
#include <bluetooth/uuid.h>
#include <bluetooth/gatt.h>
#include <bluetooth/hci.h>

#include "mible_api.h"
#include "mible_log.h"
#include "miio_user_api.h"
#include "iid.h"
#include "on_property_get.h"
#include "on_property_set.h"
#include "on_action_invoke.h"
#include "on_property_request.h"
#include "lock_service_server.h"
#include "stdio_service_server.h"
#include "cryptography/mi_crypto.h"
#include "cryptography/mi_mesh_otp.h"
#include "main.h"

#include <malloc.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <nuttx/timers/pwm.h>
#include <nuttx/timers/watchdog.h>
#include <nuttx/ioexpander/gpio.h>

#include "gwin_main_task.h"
#include "gwin_motor_task.h"
#include "gwin_general_timer.h"
#include "gwin_protocol_task.h"


#include <logging/log.h>
LOG_MODULE_REGISTER( app);


#define LOCAL_DEBUG_EN                      ( 0 )
#if LOCAL_DEBUG_EN
#define GWIN_DEBUG(...)                     USER_DEBUG(__VA_ARGS__)
#else
#define GWIN_DEBUG(...)
#endif


/* Used for factory test */
typedef struct
{
    uint8_t major;
    uint8_t minor;
    uint8_t revision;
    uint8_t padding;
    uint16_t developer_version;

    uint16_t p;
    const uint8_t *m;

    uint32_t magic;
} info_t;
const __attribute__((section(".info"))) info_t mi_info =
{
    .major = MIBLE_LIB_MAJOR,
    .minor = MIBLE_LIB_MINOR,
    .revision = MIBLE_LIB_REVISION,
    .developer_version = _DEVELOPER_VERSION,
    .p = PRODUCT_ID,
    .m = MODEL_NAME,
    .magic = 0x12345678,
};


static void *conn_timer;
static const uint8_t support_devinfo[] =
{
    DEV_INFO_HARDWARE_VERSION,
    DEV_INFO_NEW_SN,
};
static uint32_t app_periodic_time_clock = 0;
bool ota_state = 0;
bool gatt_state = 0;

static void mible_conn_timeout_handler(void *arg);
static void app_periodic_handler(void *arg);
static void scan_recv(const struct bt_le_scan_recv_info *info, struct net_buf_simple *buf);
static struct bt_le_scan_cb scan_cb = { .recv = scan_recv, };


static void user_devinfo_callback(dev_info_type_t type, dev_info_t *buf)
{
    switch (type)
    {
        case DEV_INFO_SUPPORT:
            buf->len = sizeof(support_devinfo);
            memcpy(buf->buff, support_devinfo, buf->len);
            break;
        case DEV_INFO_HARDWARE_VERSION:
            buf->len = strlen("TLSR827X");
            memcpy(buf->buff, "TLSR827X", buf->len);
            break;
        case DEV_INFO_NEW_SN:
            // maxlen 62
            buf->len = MIN(strlen("123456789/123456789/123456789/123456789/123456789/123456789/12"), 62);
            memcpy(buf->buff, "123456789/123456789/123456789/123456789/123456789/123456789/12", buf->len);
            break;
        default:
            buf->code = MI_ERR_NOT_FOUND;
            return;
    }

    buf->code = MI_SUCCESS;

    return;
}

static int user_state_process(uint8_t event, void *data)
{
    mible_status_t status;

    switch (event)
    {
        case MIBLE_USER_PROV_CB_TYPE_UNPROV:
            mible_mesh_device_scan_set(MESH_SCAN_FULL);
            miio_system_set_adv_timeout(1800000); // stop adv after 30min
            gwin_set_network_provisioned(0);
            break;
        case MIBLE_USER_PROV_CB_TYPE_PROVED:
            gwin_set_network_provisioned(1);
            break;
        case MIBLE_USER_PROV_CB_TYPE_START:
            break;
        case MIBLE_USER_PROV_CB_TYPE_COMPLETE:
            break;
        case MIBLE_USER_PROV_CB_TYPE_FAIL:
            break;
        case MIBLE_USER_PROV_CB_TYPE_RESET:     // 这里尽量少做事。APP上点击删除，会快速重启，mesh SDK不会等待应用层，无法在重启前处理，只能在重启后处理
            gwin_main_network_reset(0);
            break;
        case MIBLE_USER_SCENE_CB_ADD:
            {
                vendor_mesh_scene_store_t *scene = data;

                MI_LOG_DEBUG("Scene Add: id %d, siid %d, piid %d\n", scene->scene_id, scene->siid, scene->piid);
            }
            break;
        case MIBLE_USER_SCENE_CB_DELETE:
            {
                vendor_mesh_scene_delete_t *scene = data;

                MI_LOG_DEBUG("Scene Delete: id %d, siid %d, piid %d\n", scene->scene_id, scene->siid, scene->piid);
            }
            break;
        case MIBLE_USER_SCENE_CB_RECALL:
            {
                vendor_mesh_scene_recall_t *scene = data;

                MI_LOG_DEBUG("Scene Recall: id %d\n", scene->scene_id);
            }
            break;
        case MIBLE_USER_GAP_CB_CONNECTED:
            {
                MI_LOG_DEBUG("MIBLE_USER_GAP_CB_CONNECTED\n");

                gatt_state = 1;
                gwin_set_gatt(1);

                status = mible_timer_create(&conn_timer, mible_conn_timeout_handler, MIBLE_TIMER_SINGLE_SHOT);
                if (status)
                {
                    MI_LOG_ERROR("Unable create conn timeout timer");
                }
                else
                {
                    mible_timer_start(conn_timer, 20000, NULL);
                }
            }
            break;
        case MIBLE_USER_GAP_CB_DISCONNECTED:
            {
                MI_LOG_DEBUG("MIBLE_USER_GAP_CB_DISCONNECTED\n");

                gatt_state = 0;
                gwin_set_gatt(0);

                if (conn_timer)
                {
                    mible_timer_delete(conn_timer);
                    conn_timer = NULL;
                }
            }
            break;

        default:
            break;
    }

    return 0;
}

static void app_dfu_callback(mible_dfu_state_t state, mible_dfu_param_t *param)
{
    switch (state)
    {
        case MIBLE_DFU_STATE_START:
            ota_state = 1;
            gwin_set_ota_state(ota_state);
            MI_LOG_INFO("fragment size is %d\n", param->start.fragment_size);
            break;
        case MIBLE_DFU_STATE_UPGRADE_STATUS:
            MI_LOG_INFO("upgrad status is %x\n", param->upgrade_status.req);
            param->upgrade_status.rsp = param->upgrade_status.req;
            break;
        case MIBLE_DFU_STATE_TRANSFER:
            MI_LOG_INFO("last fragment index is %d\n", param->trans.last_index);
            break;
        case MIBLE_DFU_STATE_VERIFY:
            MI_LOG_INFO("verify result is %d\n", param->verify.value);
            break;
        case MIBLE_DFU_STATE_SWITCH:
            ota_state = 0;
            gwin_set_ota_state(ota_state);
            MI_LOG_INFO("switch to new firmware\n");
            gwin_custom_record_save_reboot_state(REBOOT_REASON_OTA);
            break;
        case MIBLE_DFU_STATE_CANCEL:
            ota_state = 0;
            gwin_set_ota_state(ota_state);
            MI_LOG_INFO("the sequence is canceled\n");
            break;
        default:
            MI_LOG_INFO("state of DFU is unknown %d\n", state);
            break;
    }
}


static void scan_recv(const struct bt_le_scan_recv_info *info, struct net_buf_simple *buf)
{
//    if (info->adv_type != BT_GAP_ADV_TYPE_ADV_NONCONN_IND)    // 这个条件限制了小程序发包，小程序无法修改，所以固件修改
//    {
//        return;
//    }

    if (buf->len < 21 && buf->len > 31)
    {
        return;
    }

    /* TODO cache filter first, should conside cpu operation times */
}


static void app_periodic_handler(void *arg)
{
    if (gwin_pass_general_clock(app_periodic_time_clock, 3000))
    {
        gwin_watch_dog_feed();

        struct mallinfo info = mallinfo();
        // LIN MARK 20230713 : MHCWB11S SDK 2.1.2 : 内存总额4000，请注意：
        // 1.正常配网需要 2800以上；2.配网期间减少 1400；3.小于2000容易死机
        if (info.fordblks <= 2800)          // 减少打印，低于某个值打印，方便看log
        {
            USER_DEBUG("Heap left:%d\n", info.fordblks);
        }

        gwin_save_general_clock(&app_periodic_time_clock);
    }
}

static void mible_conn_timeout_handler(void *arg)
{
    mible_status_t status;

    mible_timer_delete(conn_timer);
    conn_timer = NULL;

    if (get_mi_authorization() != UNAUTHORIZATION)
    {
        return;
    }

    MI_LOG_WARNING("mible conn timeout");

    status = mible_gap_disconnect(0);
    if (status)
    {
        MI_LOG_ERROR("Unable disconnect %d", status);
    }
}


static void system_tick_1ms_handler(void *arg)
{
    // LIN MARK 20230419 : MHCWB11S SDK 2.1.1 : 理论上1ms，实际上每100ms出现1次3-4ms，实测平均每秒钟-14ms左右
    // LIN MARK 20230629 : MHCWB11S SDK 2.1.2 : 调试AC发现，经常被打断5ms以上，偶尔最大打断22ms
}

void board_timerhook(void)
{
    // LIN MARK 20230419 : MHCWB11S SDK 2.1.1 : 1ms回调函数，发消息到应用层，驱动应用层唤醒处理。
    MIBLE_TASK_POST_SINGLE(system_tick_1ms_handler, NULL);

    gwin_motor_encoder_handle();
}


void get_device_id(uint8_t *pdid)
{
    int buf_len;
    msc_crt_t crt;
    uint8_t buf[512];

    buf_len = mi_mesh_otp_read(MI_DEV_CERT, buf, sizeof(buf));
    if (buf_len <= 0)
    {
        return;
    }

    mi_crypto_crt_parse_der(buf, buf_len, NULL, &crt);

    if (crt.sn.len > 8)
    {
        return;
    }

    for (int i = 0; i < crt.sn.len; i++)
    {
        pdid[crt.sn.len - 1 - i] = crt.sn.p[i];
    }
}

static void mible_dev_info_show(void)
{
//    uint8_t did[8] = { 0 };
//    uint8_t mac[6] = { 0 };
//    uint32_t did_num = 0;
//    uint32_t magic = 0;
//
//    get_device_id(did);
//    memcpy(&did_num, did, sizeof(did_num));
//    mible_gap_address_get(mac);
//    mible_otp_read(&magic, sizeof(magic), 0x0fb0);
//
//    printk("Compiled:%s %s\n", __DATE__, __TIME__);
//    printk("DID:\t%lu\n", did_num);
//    printk("MAC:\t%02x:%02x:%02x:%02x:%02x:%02x\n", mac[5], mac[4], mac[3], mac[2], mac[1], mac[0]);
//    printk("PID:\t%d\n", PRODUCT_ID);
//    printk("Model:\t%s\n", MODEL_NAME);
//    printk("Version:\t%s\n", MIBLE_LIB_AND_DEVELOPER_VERSION);
//    printk("Magic:\t%08x\n", magic);
}


/**
 * @brief 循环轮询
 */
static void board_init(void)
{
    gwin_custom_record_load();      // 加载系统参数
    gwin_board_init();              // 板载初始化

    // 部分初始化，必须等到板载初始化后才能正常
    gwin_custom_system_param_init_after_board_init();
}

static uint32_t uart_delay_clock = 0;   // 频繁读串口，while(1)运行效率严重降低
/**
 * @brief 循环轮询
 */
static void task_init(void)
{
    gwin_motor_task_init();
    gwin_protocol_task_init();
    gwin_main_task_init();

    gwin_save_general_clock(&uart_delay_clock);
}

/**
 * @brief 循环轮询
 */
static void gwin_pool_loop_handle(void)
{
    app_periodic_handler(NULL);
    if (gwin_pass_general_clock(uart_delay_clock, 40))
    {
        gwin_uart_recv_loop();
        gwin_save_general_clock(&uart_delay_clock);
    }
}

/**
 * @brief 函数入口
 */
void main(void)
{
    GWIN_DEBUG("\r\n\r\n\r\n\r\n\r\nstart\r\n\r\n\r\n\r\n\r\n");

    extern void zblue_main(void);
    zblue_main();

    if (bt_enable(NULL))
    {
        LOG_ERR("Bluetooth initialized failed\n");
        return;
    }

    // 部分初始化，必须等到蓝牙协议栈初始化后才能正常
    board_init();

    task_init();

    bt_le_scan_cb_register(&scan_cb);

    mi_service_init();
    mible_otp_init();
    mible_mesh_device_init();

    if (IS_ENABLED(CONFIG_SETTINGS))
    {
        if (settings_subsys_init())
        {
            LOG_ERR("Settings init failed!");
            return;
        }

        if (IS_ENABLED(CONFIG_BT_SETTINGS))
        {
            settings_load();
        }
    }

    mible_dev_info_show();

    miio_gatt_spec_init(on_property_set, on_property_get, on_action_invoke, 1024, 16);
    miio_dfu_callback_register(app_dfu_callback);
    miio_system_info_callback_register(user_devinfo_callback);
    miio_mesh_user_callback_register(on_property_set, on_property_get, on_action_invoke, user_state_process);

    // 部分初始化，必须等到蓝牙协议栈初始化后才能正常
    gwin_custom_system_param_init_after_ble_init();

    gwin_save_general_clock(&app_periodic_time_clock);

    while (1)
    {
        gwin_pool_loop_handle();            // 循环轮询
        gwin_general_timer_handle();        // 循环判断的定时器

        mible_tasks_exec();
        mible_mesh_device_main_thread();    // LIN MARK 20230419 : MHCWB11S SDK 2.1.1 : 应用层没有消息处理会在 mible_mesh_device_main_thread() 中死等
    }
}


